﻿using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using System.Windows.Threading;

namespace VideoTools.Commands
{
    public class DelegateCommand : NotifyPropertyBase, ICommand
    {
        
        private readonly object _syncLock = new object();
        private readonly Func<object, bool> _mCanExecute;
        private readonly Action<object> _executeAction;
        private bool _isExecuting;

        
        public DelegateCommand(Action<object> execute, Func<object, bool> canExecute)
        {
            var callback = execute ?? throw new ArgumentNullException(nameof(execute));
            _mCanExecute = canExecute;

            _executeAction = parameter =>
            {
                try
                {
                    var canExecuteAction = _mCanExecute?.Invoke(parameter) ?? true;

                    if (canExecuteAction)
                    {
                        callback(parameter);
                    }
                }
                catch { }
            };
        }

        public DelegateCommand(Action<object> execute)
            : this(execute, null)
        {
        }

        

        
        public event EventHandler CanExecuteChanged
        {
            add => CommandManager.RequerySuggested += value;
            remove => CommandManager.RequerySuggested -= value;
        }

        

        

        
        public bool IsExecuting
        {
            get
            {
                lock (_syncLock)
                {
                    return _isExecuting;
                }
            }
            private set
            {
                lock (_syncLock)
                {
                    _isExecuting = value;
                }
            }
        }



        public KeyGesture DefaultKeyGesture { get; } = null;


        [DebuggerStepThrough]
        public bool CanExecute(object parameter)
        {
            if (IsExecuting)
            {
                return false;
            }

            try
            {
                return _mCanExecute == null || _mCanExecute(parameter);
            }
            catch
            {
                return false;
            }
        }

        public bool CanExecute()
        {
            return CanExecute(null);
        }

        public void Execute(object parameter)
        {
            ExecuteAsync(parameter);
        }

        public void Execute()
        {
            ExecuteAsync(null);
        }

        public bool TryExecute(object parameter = null)
        {
            if (CanExecute(parameter))
            {
                if (IsExecuting)
                {
                    return false;
                }

                ExecuteAsync(parameter);
                return true;
            }

            return false;
        }

        public ConfiguredTaskAwaitable ExecuteAsync(object parameter)
        {
            return Task.Run(async () =>
            {
                if (IsExecuting)
                {
                    return;
                }

                try
                {
                    IsExecuting = true;
                    await System.Windows.Application.Current.Dispatcher.BeginInvoke(_executeAction, DispatcherPriority.Normal,
                        parameter);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine($"Could not execute command. {ex.Message}");
                    throw;
                }
                finally
                {
                    IsExecuting = false;
                    CommandManager.InvalidateRequerySuggested();
                }
            }).ConfigureAwait(true);
        }

        public ConfiguredTaskAwaitable ExecuteAsync()
        {
            return ExecuteAsync(null);
        }


        
    }
}
